package cn.juque.lucenecandy.interceptor;

import cn.juque.common.base.BaseResponseVO;
import cn.juque.common.utils.HttpClientUtil;
import cn.juque.lucenecandy.core.constants.StrConstant;
import cn.juque.lucenecandy.core.enums.LuceneCandyMsgEnum;
import cn.juque.lucenecandy.core.utils.SignatureUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * @author juque
 * @version 1.0.0
 * <ul>
 *     <li>CheckSignatureInterceptor</li>
 * </ul>
 * @date 2023-12-27
 **/
@Slf4j
public class CheckSignatureInterceptor implements HandlerInterceptor {
    /**
     * Intercept the execution of a handler. Called after HandlerMapping determined
     * an appropriate handler object, but before HandlerAdapter invokes the handler.
     * <p>DispatcherServlet processes a handler in an execution chain, consisting
     * of any number of interceptors, with the handler itself at the end.
     * With this method, each interceptor can decide to abort the execution chain,
     * typically sending an HTTP error or writing a custom response.
     * <p><strong>Note:</strong> special considerations apply for asynchronous
     * request processing. For more details see
     * {@link AsyncHandlerInterceptor}.
     * <p>The default implementation returns {@code true}.
     *
     * @param request  current HTTP request
     * @param response current HTTP response
     * @param handler  chosen handler to execute, for type and/or instance evaluation
     * @return {@code true} if the execution chain should proceed with the
     * next interceptor or the handler itself. Else, DispatcherServlet assumes
     * that this interceptor has already dealt with the response itself.
     * @throws Exception in case of errors
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String queryString = request.getQueryString();
        Map<String, String> map = HttpClientUtil.splitParam(queryString);
        if (!map.containsKey(StrConstant.SIGNATURE)
                || !map.containsKey(StrConstant.TIMESTAMP) || !map.containsKey(StrConstant.RANDOM_STR)) {
            BaseResponseVO<Boolean> responseVO = new BaseResponseVO<>(LuceneCandyMsgEnum.SIGNATURE_CHECK_FAIL, null);
            this.print(response, responseVO);
            return false;
        }
        String signature = String.valueOf(map.get(StrConstant.SIGNATURE));
        Long timestamp = Long.parseLong(map.get(StrConstant.TIMESTAMP));
        String randomStr = String.valueOf(map.get(StrConstant.RANDOM_STR));
        boolean flag = SignatureUtil.checkSignature(timestamp, randomStr, signature);
        if (!flag) {
            BaseResponseVO<Boolean> responseVO = new BaseResponseVO<>(LuceneCandyMsgEnum.SIGNATURE_CHECK_FAIL, null);
            this.print(response, responseVO);
        }
        return flag;
    }

    /**
     * 输出消息
     *
     * @param response   HttpServletResponse
     * @param responseVO BaseResponseVO
     */
    private void print(HttpServletResponse response, BaseResponseVO<?> responseVO) {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        try {
            response.getWriter().write(new ObjectMapper().writeValueAsString(responseVO));
            response.getWriter().flush();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }
}
